/* 
 * File:   CSString.c
 * Author: Muhammad Anis
 */

#include <stdlib.h>
#include "CSString.h"

/**
 * Get Length of CSString
 * @param input CSString input
 * @return Length of CSString
 */
int GetCSStringLength(CSString input) {
    int i = 0;
    if (input) {
        while (input[i] != '\0') { // while character is not null
            i++;
        }
    } else {
        printf("Null Pointer Exception"); // null string input
    }
    return i;

}

/**
 * Get character at specified position
 * @param text Input CSString
 * @param pos Position
 * @return Character at Position
 */
char GetCharAt(CSString text, int pos) {
    if (pos < GetCSStringLength(text)) {
        return text[pos];
    }
}

/**
 * Get Substring of String from startIdx to lastIdx
 * @param input String input
 * @param startIdx Start index for substring
 * @param lastIdx End index for substring, if less than 0, start from end
 * @return Substring of String
 */
void CopyString(CSString input, CSString output, int startIdx, int lastIdx) {
    if (startIdx < 0) {
        printf("Start index must >= 0");
    } else {
        if (lastIdx < 0) {
            lastIdx = GetCSStringLength(input) - lastIdx;
        }
        if (lastIdx <= GetCSStringLength(input) && lastIdx >= startIdx) {
            int i;
            for (i = startIdx; i <= lastIdx; i++) {
                output[i - startIdx] = input[i]; // copy character
            }
            output[lastIdx - startIdx + 1] = '\0'; // close string with null character
        } else {
            printf("Out Of index Exception %s %d %d", input, startIdx, lastIdx);
        }
    }
}

/**
 * Check if two string is equal
 * @param c1 First CSString
 * @param c2 Second CSString
 * @return true if c1=c2 case sensitive
 */
boolean EqCSString(CSString c1, CSString c2) {
    if (GetCSStringLength(c1) == GetCSStringLength(c2)) {
        int i;
        for (i = 0; i < GetCSStringLength(c1); i++) {
            if (GetCharAt(c1, i) != GetCharAt(c2, i)) { // check character
                return false; // not same, returns false
            }
        }
        return true;
    } else {
        return false;
    }
}

/**
 * Split Text Based on regex given
 * @param text Input text
 * @param regex output (array of string)
 */
int Split(CSString text, CSString* result, CSString regex) {
    int j, i = 0, head = -1;
    int stringLength = GetCSStringLength(text);
    for (j = 0; j < 100; j++) {
        result[j] = "\0"; // initialize result
    }
    while (i < stringLength) {
        Trim(text);
        stringLength = GetCSStringLength(text);
        int searchIdx = BMMatch(text, regex);
        if (searchIdx != -1) { // separator index found in text
            head++;
            if (head < 100) {
                result[head] = (char*) malloc((searchIdx - i + 1) * sizeof (char));
                CopyString(text, result[head], i, searchIdx - 1); // copy string from text to result
            }
            i = searchIdx + 1;
            if (i < stringLength) {
                CSString temp;
                temp = (char*) malloc((stringLength - i + 1) * sizeof (char));
                CopyString(text, temp, i, stringLength - 1);
                Dealocate(text);
                text = temp;
                stringLength = GetCSStringLength(temp);
                i = 0;
            }
        } else {
            head++;
            result[head] = (char*) malloc((stringLength - 1 - i + 2) * sizeof (char));
            CopyString(text, result[head], i, stringLength - 1);
            i = stringLength;
        }
    }
    return head + 1;
}

/**
 * Remove white spaces from start and end of text
 * @param input Input string
 * @return trimmed string
 */
char* Trim(char* input) {
    char* end;
    while (IsSpace(*input)) {
        input++;
    }
    if (*input == 0) {
        return input;
    }
    end = input + GetCSStringLength(input) - 1;
    while (end > input && IsSpace(*end)) {
        end--;
    }
    if (end != input + GetCSStringLength(input) - 1) {
        *(end + 1) = 0;
    }
    return input;
}

/**
 * Get index of pattern occurence in text
 * @param text Input text
 * @param pattern Pattern
 * @return first found index value
 */
int BMMatch(CSString text, CSString pattern) {
    int last[128];
    BuildLast(pattern, last);
    int n = GetCSStringLength(text);
    int m = GetCSStringLength(pattern);
    int i = m - 1;
    if (i > n - 1) {
        return -1;
    } else {
        int j = m - 1;
        do {
            if (GetCharAt(pattern, j) == GetCharAt(text, i)) {
                if (j == 0) {
                    return i;
                } else {
                    i--;
                    j--;
                }
            } else {
                int lo = last[GetCharAt(text, i)];
                i = i + m - MinValue(j, 1 + lo);
                j = m - 1;
            }
        } while (i <= n - 1);
        return -1;
    }
}

/**
 * Create array of last occurence in array
 * @param pattern
 * @return 
 */
void BuildLast(CSString pattern, int* last) {
    int i;
    for (i = 0; i < 128; i++) {
        last[i] = -1;
    }
    for (i = 0; i < GetCSStringLength(pattern); i++) {
        last[GetCharAt(pattern, i)] = i;
    }
}

/**
 * Get Min Value of two integer
 * @param a first value
 * @param b second value
 * @return the most minimal value between a and b
 */
int MinValue(int a, int b) {
    if (a < b) {
        return a;
    }
    return b;
}

/**
 * Check if a character is white space
 * @param c character
 * @return  true if c is one of white spaces
 */
boolean IsSpace(char c) {
    return (c == '\n' || c == '\r' || c == ' ' || c == '\t');
}

/**
 * Dealocate char*
 * @param c char*
 */
void Dealocate(char* c) {
    free(c);
}

/**
 * Append String
 * @param first first string
 * @param second second string
 * @return first+second
 */
CSString AppendString(CSString first, CSString second) {
    CSString ret_val = "";
    int first_length = GetCSStringLength(first);
    int length = first_length + GetCSStringLength(second);
    ret_val = (char*) malloc((length + 1) * sizeof (char));
    if (ret_val) {
        int i;
        for (i = 0; i < first_length; i++) {
            ret_val[i] = first[i];
        }
        for (i = first_length; i < length; i++) {
            ret_val[i] = second[i - first_length];
        }
        ret_val[length] = '\0';
    }
    return ret_val;
}

